///
/// ArmyBodger 3
/// (c)2010 richyp
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ArmyBodger.Core {
    /// <summary>
    /// Represents an Entry from an Army Book
    /// </summary>
    public class Entry {
        /// <summary>
        /// Default Constructor
        /// </summary>
        public Entry() {
            this.Upgrades = new UpgradeCollection();
            this.Units = new UnitCollection();
            this.PossibleUnits = new UnitCollection();
            this.Tags = new Dictionary<string, string>();
            this.CostOffset = 0;
            this.WargearCostOffset = 0;
            this.IsSpecial = false;
        }
        /// <summary>
        /// The Name of the Entry
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// Army Book Category the Entry belongs to, e.g. HQ/Core etc..
        /// </summary>
        public string Category { get; set; }
        /// <summary>
        /// Is this choice a special character?
        /// </summary>
        public bool IsSpecial { get; set; }
        /// <summary>
        /// If the unit has an odd cost (eg 5 18 point models costs 100 points) cost offset will pad the cost
        /// </summary>
        public int CostOffset { get; set; }
        /// <summary>
        /// Some units have wargear that is priced nonstandardly (eg 2nd special gun is free), the difference will be made up here
        /// </summary>
        public int WargearCostOffset { get; set; }
        /// <summary>
        /// Collection of "Tags", that can be used by the script engine
        /// </summary>
        public Dictionary<string,string> Tags { get; set; }
        /// <summary>
        /// Possible Entry wide upgrades for this Entry
        /// </summary>
        public UpgradeCollection Upgrades { get; set; }
        /// <summary>
        /// The Current units in this Entry
        /// </summary>
        public UnitCollection Units { get; set; }
        /// <summary>
        /// Collection of all possible units this entry provides
        /// </summary>
        public UnitCollection PossibleUnits { get; set; }
        /// <summary>
        /// Called when the entry is added to the list
        /// </summary>
        public string OnAddEvent { get; set; }
        /// <summary>
        /// Set the value of the specified scriptable tag
        /// </summary>
        /// <param name="name">Name of the tag, e.g. foo</param>
        /// <param name="value">Value of the tag, e.g. bar</param>
        public void SetTag(string name, string value) {
            if (!this.Tags.ContainsKey(name)) {
                this.Tags.Add(name, value);
            } else {
                this.Tags[name] = value;
            }
        }
        /// <summary>
        /// Get the value of the named tag
        /// </summary>
        /// <param name="name">The name of the tag to get the value from</param>
        /// <returns>The value of the tag "name"</returns>
        public string GetTag(string name) {
            return this.Tags[name];
        }
        /// <summary>
        /// Removed the named tag from this Entry
        /// </summary>
        /// <param name="name">The name of the tag to remove</param>
        public void RemoveTag(string name) {
            this.Tags.Remove(name);
        }
        /// <summary>
        /// Calculate the total cost of this Entry
        /// </summary>
        public int Cost {
            get {
                int cost = 0;
                cost += CostOffset;
                cost += WargearCostOffset;
                foreach (Unit unit in this.Units) {
                    cost += unit.Cost;
                }
                foreach (Upgrade upgrade in this.Upgrades) {
                    if (upgrade.Applied) {
                        if (upgrade.CostIsUnitWide) {
                            cost += upgrade.Cost;
                        } else {
                            cost += this.Units.Count * upgrade.Cost;
                        }
                    }
                }
                return cost;
            }
        }
        /// <summary>
        /// If this unit has a primary entry (e.g. Space Marine) return it, so that
        /// the +/- buttons can be used on the list control
        /// </summary>
        /// <returns></returns>
        public string GetPrimary() {
            foreach (Unit unit in this.Units) {
                if (unit.IsPrimary) {
                    return unit.Name;
                }
            }
            return "";
        }
        /// <summary>
        /// Create a new instance of Entry, based on the ArmyBook's description
        /// Note: Always use this when creating entries, either in C# or from
        /// Python, to avoide referencing problems
        /// </summary>
        /// <returns>A newly populated Entry object</returns>
        public Entry CreateDefaultEntry() {
            Entry entry = this.Copy();
            foreach (Unit unit in this.PossibleUnits) {
                if (unit.InitialQuantity > 0) {
                    for (int i = 0; i < unit.InitialQuantity; i++) {
                        entry.Units.Add(unit.Copy(entry));
                    }
                }
            }
            return entry;
        }
        /// <summary>
        /// Add one of the possible units to this Entry, as long as 
        /// the conditions allow it
        /// </summary>
        /// <param name="name">
        /// The name of the Entry, as it appears in PossibleEntries
        /// </param>
        /// <returns>True if the addition was successful otherwise false</returns>
        public bool AddUnit(string name) {
            bool possible = false;
            foreach (Unit unit in this.PossibleUnits) {
                if (unit.Name == name && unit.IsAddPermitted(this) ) {
                    possible = true;
                    break;
                }
            }
            if (!possible) {
                return false;
            }
            Unit possibleUnit = this.PossibleUnits.GetUnitsByName(name)[0];
            if (possibleUnit.Replaces == null || possibleUnit.Replaces.Length == 0) {
                this.Units.Add(possibleUnit.Copy(this));
                return true;
            } else if (possibleUnit.Replaces != null) {
                if (this.Units.GetUnitsByName(possibleUnit.Replaces).Count > 0) {
                    Unit removeMe = this.Units.GetUnitsByName(possibleUnit.Replaces)[0];
                    this.Units.Remove(removeMe);
                    this.Units.Add(possibleUnit.Copy(this));
                    return true;
                } else {
                    return false;
                }
            }
            return false;
        }
        /// <summary>
        /// Clone this entry
        /// </summary>
        /// <returns>A new instance of this Entry with the same values etc..</returns>
        public Entry Copy() {
            return Entry.Copy(this);
        }
        /// <summary>
        /// Static Copy method to Clone without an instance (useful for scripts)
        /// </summary>
        /// <param name="source">The Entry object to clone</param>
        /// <returns>A Clone of the "source" Entry</returns>
        public static Entry Copy(Entry source) {
            Entry entry = new Entry();
            entry.Name = source.Name;
            entry.Category = source.Category;
            entry.IsSpecial = source.IsSpecial;
            entry.OnAddEvent = source.OnAddEvent;
            foreach (Upgrade upgrade in source.Upgrades) {
                entry.Upgrades.Add(upgrade.Copy());
            }
            foreach (Unit unit in source.Units) {
                entry.Units.Add(unit.Copy(entry));
            }
            foreach (Unit unit in source.PossibleUnits) {
                entry.PossibleUnits.Add(unit.Copy(entry));
            }
            foreach (string tagKey in source.Tags.Keys) {
                entry.Tags.Add(tagKey, source.Tags[tagKey]);
            }
            return entry;
        }
        /// <summary>
        /// Remove all the upgrades that belong to the specified category
        /// Note: Useful for scripting, e.g. unequip all weapons before 
        /// equipping a new one
        /// </summary>
        /// <param name="category">The Category of Upgrades to unequip</param>
        public void RemoveUpgradesByCategory(string category) {
            foreach (Upgrade upgrade in this.Upgrades) {
                if (upgrade.Equipment.Category == category) {
                    upgrade.Applied = false;
                }
            }
        }
        /// <summary>
        /// Remove the upgrade "name"
        /// </summary>
        /// <param name="name">The name of the upgrade to remove</param>
        public void RemoveUpgradeByName(string name) {
            foreach (Upgrade upgrade in this.Upgrades) {
                if (upgrade.Equipment.Name == name) {
                    upgrade.Applied = false;
                }
            }
        }
        
        public int NumberOfUnitsEquipped(string unitType, string equipment)
        {
            int numberOfUnitsEquipped = 0;

            foreach (Unit unit in this.Units)
            {
                if (unit.Name != unitType)
                    continue;

                if (unit.Equipment[equipment] != null && unit.Equipment[equipment].Equipped == true)
                {
                    numberOfUnitsEquipped++;
                    continue;
                }

                foreach (KeyValuePair<string, EquipmentOptionCollection> kvp in unit.Archetypes)
                {
                    if(this.Upgrades[kvp.Key].Applied)
                        if (kvp.Value[equipment] != null && kvp.Value[equipment].Equipped == true)
                        {
                            numberOfUnitsEquipped++;
                            continue;
                        }
                }
            }

            return numberOfUnitsEquipped;
        }
        /// <summary>
        /// Get the user-friendly description of this Entry (used by the list control)
        /// </summary>
        public string Description {
            get {
                StringBuilder description = new StringBuilder();
                Dictionary<string, int> counts = new Dictionary<string, int>();
                Dictionary<string, int> equippedCount = new Dictionary<string, int>();
                List<string> unitNames = new List<string>();
                List<string> equipmentNames = new List<string>();
                string primaryName = null;
                foreach (Unit unit in this.Units) {
                    if (counts.ContainsKey(unit.Name)) {
                        counts[unit.Name]++;                        
                    } else {
                        unitNames.Add(unit.Name);
                        counts.Add(unit.Name, 1);
                        if (unit.IsPrimary) {
                            primaryName = unit.Name;
                        }
                    }
                    foreach (EquipmentOption option in unit.Equipment) {
                        if (option.Equipped) {
                            if (!equipmentNames.Contains(option.Description)) {
                                if (!option.Fixed && ! option.Default) {
                                    equipmentNames.Add(option.Description);
                                    equippedCount.Add(option.Description, 1);
                                }
                            } else {
                                equippedCount[option.Description]++;
                            }
                        } else if (option.Replacement != null) {
                            if (!equipmentNames.Contains(option.Replacement.Description)) {
                                equipmentNames.Add(option.Replacement.Description);
                                equippedCount.Add(option.Replacement.Description, 1);
                            } else {
                                equippedCount[option.Replacement.Description]++;
                            }
                        }
                    }
                }
                unitNames.Sort();
                equipmentNames.Sort();

                if (primaryName != null) {
                    description.Append(counts[primaryName] + " x " + primaryName + ",");
                }
                foreach (string unitName in unitNames) {
                    if (unitName != primaryName) {
                        description.Append(counts[unitName] + " x " + unitName + ",");
                    }
                }
                description.Remove(description.Length - 1, 1);
                description.Append(" (");
                foreach (Upgrade upgrade in this.Upgrades) {
                    if (upgrade.Applied) {
                        description.Append(upgrade.Equipment.Name + ",");
                    }
                }
                description.Remove(description.Length - 1, 1);
                foreach (string equipment in equipmentNames) {                    
                    description.Append(equippedCount[equipment] + " x " + equipment + ",");
                }
                if (description.ToString().IndexOf("(") > -1) {
                    description.Append(")");
                }
                return description.ToString();
            }
        }
        /// <summary>
        /// Get an XML representation of this Entry, so that it can be persisted
        /// Note: Used by the Save List method
        /// </summary>
        /// <returns>XML String represnting this Entry</returns>
        public string ToXml() {
            StringBuilder xml = new StringBuilder();
            xml.Append("  <entry name=\"" + this.Name + "\" category=\"" + this.Category + "\">\n");
            foreach (Upgrade upgrade in this.Upgrades) {
                if (upgrade.Applied) {
                    xml.Append("    <upgrade name=\"" + upgrade.Equipment.Name + "\"/>\n");
                }
            }
            xml.Append("    <tags>\n");
            foreach (string key in this.Tags.Keys) {
                xml.Append("      <tag name=\"" + key + "\"><![CDATA[" + this.Tags[key] + "]]></tag>\n"); 
            }
            xml.Append("    </tags>\n");
            xml.Append("    <units>\n");
            foreach (Unit unit in this.Units) {
                xml.Append("      <unit name=\"" + unit.Name + "\">\n");
                foreach (EquipmentOption option in unit.Equipment) {
                    if (option.Equipped) {
                        xml.Append("        <option name=\"" + option.Equipment.Name + "\">\n");
                        if (option.ReplacementApplied) {
                            xml.Append("          <replacement>" + option.Replacement.Equipment.Name + "</replacement>\n");
                        }
                        xml.Append("        </option>\n");
                    }
                }
                xml.Append("      </unit>\n");
            }
            xml.Append("    </units>\n");
            xml.Append("  </entry>\n");
            return xml.ToString();
        }

        public override string ToString()
        {
            return this.Name;
        }

    }
}
